import tkinter as tk
from tkinter import messagebox, ttk

from ..core.data_loader import DataLoader
from ..models.data_source import SourceType
from ..utils.logger import logger


class MappingDialog(tk.Toplevel):
    """字段映射对话框"""

    def __init__(
        self,
        parent,
        data_fields,
        source_type,
        source_path,
        existing_mapping=None,
        db_config=None,
    ):
        """初始化
        Args:
            parent: 父窗口
            data_fields: 数据项列表
            source_type: 数据源类型
            source_path: 数据源路径
            existing_mapping: 现有映射关系
            db_config: 数据库配置信息
        """
        super().__init__(parent)
        self.title("字段映射")
        self.geometry("500x600")
        self.resizable(False, False)

        # 保存参数
        self.data_fields = data_fields
        self.source_type = source_type
        self.source_path = source_path
        self.existing_mapping = existing_mapping or {}
        self.db_config = db_config

        # 初始化结果
        self.result = None

        # 加载数据源字段
        self.source_fields = self._load_source_fields()

        # 初始化界面
        self._init_ui()

        # 使用 after 方法延迟执行居中显示
        self.after(10, lambda: parent.app.center_dialog(self))

    def _init_ui(self):
        """初始化界面"""
        # 创建主框架
        main_frame = ttk.Frame(self, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 创建工具栏
        toolbar = ttk.Frame(main_frame)
        toolbar.pack(fill=tk.X, pady=(0, 10))

        # 添加自动映射按钮
        ttk.Button(toolbar, text="自动映射", command=self._auto_mapping).pack(
            side=tk.LEFT, padx=5
        )
        ttk.Button(toolbar, text="清除映射", command=self._clear_mapping).pack(
            side=tk.LEFT, padx=5
        )

        # 创建映射表格
        self._create_mapping_table(main_frame)

        # 创建按钮区域
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=(10, 0))

        ttk.Button(btn_frame, text="确定", command=self._on_ok).pack(
            side=tk.LEFT, padx=5
        )
        ttk.Button(btn_frame, text="取消", command=self._on_cancel).pack(
            side=tk.LEFT, padx=5
        )

    def _create_mapping_table(self, parent):
        """创建映射表格"""
        # 创建表格容器
        table_container = ttk.Frame(parent)
        table_container.pack(fill=tk.BOTH, expand=True)

        # 创建表格
        columns = ("data_field", "source_field")
        self.mapping_tree = ttk.Treeview(
            table_container, columns=columns, show="headings", height=15
        )

        # 设置列标题
        self.mapping_tree.heading("data_field", text="数据项")
        self.mapping_tree.heading("source_field", text="数据源字段")

        # 设置列宽
        self.mapping_tree.column("data_field", width=200)
        self.mapping_tree.column("source_field", width=200)

        # 添加滚动条
        scrollbar = ttk.Scrollbar(
            table_container, orient=tk.VERTICAL, command=self.mapping_tree.yview
        )
        self.mapping_tree.configure(yscrollcommand=scrollbar.set)

        # 布局
        self.mapping_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 创建Combobox字典
        self.field_combos = {}

        # 添加数据项并回显现有映射
        for field in self.data_fields:
            # 插入数据项
            item = self.mapping_tree.insert(
                "",
                tk.END,
                values=(f"{field['name']} ({field['code']})", ""),
            )

            # 创建Combobox
            combo = ttk.Combobox(
                table_container, values=self.source_fields, state="readonly"
            )
            combo.bind(
                "<<ComboboxSelected>>",
                lambda e, item=item: self._on_combo_select(e, item),
            )

            # 回显现有映射
            if self.existing_mapping:
                # 查找当前字段是否有映射
                source_field = next(
                    (
                        src
                        for src, tgt in self.existing_mapping.items()
                        if tgt == field["code"]
                    ),
                    None,
                )
                if source_field and source_field in self.source_fields:
                    combo.set(source_field)
                    self.mapping_tree.set(item, "source_field", source_field)

            # 保存Combobox引用
            self.field_combos[item] = combo

        # 绑定表格点击事件
        self.mapping_tree.bind("<<TreeviewSelect>>", self._on_tree_select)

    def _load_source_fields(self):
        """加载数据源字段"""
        try:
            loader = DataLoader(
                self.source_type,
                self.source_path,
                self.db_config if self.source_type == SourceType.DATABASE else None,
            )
            fields = loader.get_fields()
            logger.info(f"加载数据源字段: {fields}")
            return fields
        except Exception as e:
            logger.error(f"加载数据源字段失败: {str(e)}", exc_info=True)
            return []

    def _on_tree_select(self, event):
        """表格选择事件处理"""
        selection = self.mapping_tree.selection()
        if not selection:
            return

        item = selection[0]
        combo = self.field_combos.get(item)
        if not combo:
            return

        # 获取单元格位置
        bbox = self.mapping_tree.bbox(item, "source_field")
        if not bbox:
            return

        # 调整Combobox位置和大小
        x, y, w, h = bbox
        combo.place(
            x=x + self.mapping_tree.winfo_x() + 1,
            y=y + self.mapping_tree.winfo_y() + 1,
            width=w - 2,
            height=h - 2,
        )
        combo.lift()
        combo.focus_set()

    def _on_combo_select(self, event, item):
        """下拉框选择事件处理"""
        combo = event.widget
        value = combo.get()
        self.mapping_tree.set(item, "source_field", value)
        combo.place_forget()

    def _on_ok(self):
        """确定按钮处理"""
        # 收集映射关系
        mapping = {}
        for item in self.mapping_tree.get_children():
            data_field = self.mapping_tree.item(item)["values"][0]
            source_field = self.mapping_tree.item(item)["values"][1]

            if source_field:  # 只保存有映射的字段
                # 从数据项名称中提取代码
                code = data_field.split("(")[-1].rstrip(")")
                # 修改映射方向：source_field -> code
                mapping[source_field] = code

        self.result = mapping
        self.destroy()

    def _on_cancel(self):
        """取消按钮处理"""
        self.destroy()

    def _auto_mapping(self):
        """自动映射字段"""
        try:
            # 清除现有映射
            self._clear_mapping()

            # 创建字段名称和代码的映射字典
            field_map = {}
            for field in self.data_fields:
                # 将字段名称和代码都作为key，值为字段本身
                name = field["name"].lower().strip()
                code = field["code"].lower().strip()
                field_map[name] = field
                field_map[code] = field
                # 添加去除特殊字符后的名称
                clean_name = "".join(c for c in name if c.isalnum())
                clean_code = "".join(c for c in code if c.isalnum())
                if clean_name:
                    field_map[clean_name] = field
                if clean_code:
                    field_map[clean_code] = field

            # 遍历数据源字段，尝试匹配
            matched_count = 0
            for source_field in self.source_fields:
                source_name = source_field.lower().strip()
                clean_source = "".join(c for c in source_name if c.isalnum())

                # 1. 尝试直接匹配
                matched_field = None
                if source_name in field_map:
                    matched_field = field_map[source_name]
                elif clean_source in field_map:
                    matched_field = field_map[clean_source]

                if matched_field:
                    self._set_mapping(matched_field, source_field)
                    matched_count += 1
                    continue

                # 2. 尝试模糊匹配
                best_match = None
                best_ratio = 0
                for field in self.data_fields:
                    # 计算与名称的相似度
                    name_ratio = self._similarity(source_name, field["name"].lower())
                    # 计算与代码的相似度
                    code_ratio = self._similarity(source_name, field["code"].lower())
                    # 计算清理后的相似度
                    clean_name_ratio = self._similarity(
                        clean_source,
                        "".join(c for c in field["name"].lower() if c.isalnum()),
                    )
                    clean_code_ratio = self._similarity(
                        clean_source,
                        "".join(c for c in field["code"].lower() if c.isalnum()),
                    )
                    # 取最高的相似度
                    ratio = max(
                        name_ratio, code_ratio, clean_name_ratio, clean_code_ratio
                    )
                    if ratio > 0.8 and ratio > best_ratio:  # 设置相似度阈值
                        best_ratio = ratio
                        best_match = field

                if best_match:
                    self._set_mapping(best_match, source_field)
                    matched_count += 1

            if matched_count > 0:
                logger.info(f"自动映射完成，成功匹配 {matched_count} 个字段")
                messagebox.showinfo(
                    "成功", f"自动映射完成，成功匹配 {matched_count} 个字段"
                )
            else:
                logger.warning("未找到可匹配的字段")
                messagebox.showwarning("提示", "未找到可匹配的字段")

        except Exception as e:
            logger.error(f"自动映射失败: {str(e)}", exc_info=True)
            messagebox.showerror("错误", f"自动映射失败: {str(e)}")

    def _similarity(self, s1: str, s2: str) -> float:
        """计算两个字符串的相似度"""
        from difflib import SequenceMatcher

        # 如果字符串为空，返回0
        if not s1 or not s2:
            return 0

        return SequenceMatcher(None, s1, s2).ratio()

    def _clear_mapping(self):
        """清除所有映射"""
        for item in self.mapping_tree.get_children():
            self.mapping_tree.set(item, "source_field", "")
            combo = self.field_combos.get(item)
            if combo:
                combo.set("")

    def _set_mapping(self, field, source_field):
        """设置字段映射"""
        for item in self.mapping_tree.get_children():
            values = self.mapping_tree.item(item)["values"]
            if f"{field['name']} ({field['code']})" == values[0]:
                self.mapping_tree.set(item, "source_field", source_field)
                combo = self.field_combos.get(item)
                if combo:
                    combo.set(source_field)
                break
